<?php


namespace models\Business;


use Common\AccessToken4Api;
use Hashids\Hashids;
use models\DAO\MemberModel;
use models\DAO\MemberoauthModel;
use models\Exception\Business\MemberException;
use models\Exception\Business\MemberoauthException;
use models\Exception\Business\OauthException;
use models\Exception\DAO\ModelDriverException;
use models\Exception\DAO\ModelException;
use models\Exception\DAO\ModelReflectionException;
use models\Exception\DAO\ModelSqlException;
use models\Exception\Transformer\TransformerException;
use models\Service\MemberoauthService;
use models\Service\MemberService;
use models\Service\OauthService;
use Yaf\Exception;
use Yaf\Registry;
use youliPhpLib\Common\PwdAss;
use youliPhpLib\Common\StringOperation;

class Oauth
{
    static public function String2Number($string = '') {
        if (!is_string($string))
            return 0;

        $arr = preg_split('/(?<!^)(?!$)/u', $string);
        $num_str = '';

        foreach($arr as &$v){
            $num_str .= ord($v);
//            $temp = unpack('H*', $v);
//            $v = base_convert($temp[1], 16, 2);
//            unset($temp);
        }
//        $num_str = implode('', $arr);

//        return (int)$num_str;
        return $num_str;

    }

    /**
     * 第三方登陆账号绑定手机号码
     * @param $parameters
     * @throws ModelSqlException
     * @throws OauthException
     */
    static public function bindMobile($parameters) {
        $conn = MemberModel::getInstance()->getConn();
        $conn->beginTransaction();
        $commit = false;
        try {
            if (! isset($parameters['mobile']) || ! isset($parameters['from_api']) || $parameters['from_api'] !== true)
                throw new OauthException("非法请求", 400);
            // 判断要绑定的手机号码是否已被注册
            $mobile = MemberService::getInstance()->getList(1, 999, 'desc', 'id', [
                'groupOp' => 'OR',
                'rules' => [
                    ['field' => 'mobile', 'op' => 'eq', 'data' => $parameters['mobile']],
                    ['field' => 'id', 'op' => 'eq', 'data' => $parameters['id']]]
            ]);

            $want_to_bind_member_id = $wait_for_check_member_id = $want_to_bind_type =
            $want_to_bind_open_id = $want_to_bind_oauth_id = '';
            foreach ($mobile['data'] as $idx => $member_info) {
                // 要求绑定的手机号码已经被注册
                if (! empty($member_info['mobile']) && $member_info['id'] != $parameters['id'])
                    $wait_for_check_member_id = $member_info['id'];
                // 要求绑定的会员，没有绑定过手机号码
                else if (empty($member_info['mobile']) && $member_info['id'] == $parameters['id'])
                    $want_to_bind_member_id = $member_info['id'];
                // 要求绑定的会员已经绑定过手机号码，且绑定的手机号码就是当前要绑定的手机号码
                else if ($member_info['mobile'] == $parameters['mobile'] && $member_info['id'] == $parameters['id'])
                    throw new OauthException('你已经绑定过该手机号了,请前往修改登陆密码', 400);
            }

            // 根据想要绑定手机号码的会员，查询其第三方登陆平台的open_id与type是什么
            if ($want_to_bind_member_id) {
                $sql = "SELECT mo.`oauth_id`, o.`type`, o.`open_id` FROM `memberoauth` AS mo LEFT JOIN `oauth` AS o ON mo.`oauth_id` = o.`id` WHERE mo.member_id = '" . $want_to_bind_member_id . "' AND o.`type` = '" . $parameters['type'] . "' LIMIT 1";
                $member_oauth = MemberoauthModel::getInstance()->query($sql);
                if (! empty($member_oauth)) {
                    $want_to_bind_type = $member_oauth[0]['type'];
                    $want_to_bind_open_id = $member_oauth[0]['open_id'];
                    $want_to_bind_oauth_id = $member_oauth[0]['oauth_id'];
                }
            }

            $update_member_oauth_sql = $update_member_sql = '';
            // 若想要绑定的手机号码已经在member中注册
            // 1、绑定过其他平台的第三方账号，允许绑定
            if ($wait_for_check_member_id && $want_to_bind_open_id && $want_to_bind_type) {
                $sql = "SELECT mo.`oauth_id` FROM `memberoauth` AS mo LEFT JOIN `oauth` AS o ON mo.`oauth_id` = o.`id` WHERE mo.member_id = '" . $wait_for_check_member_id .
                    "' AND o.type = '" . $want_to_bind_type . "' AND o.open_id != '" . $want_to_bind_open_id . "'";
                $member_oauth = MemberoauthModel::getInstance()->query($sql);
                // 已经绑定过其他同平台的第三方账号
                if (! empty($member_oauth)) {
                    $type = $parameters['type'] == 2 ? '微信账号' : ($parameters['type'] == 3 ? 'qq账号' : '第三方账号');
                    throw new OauthException($parameters['mobile'] . '已经绑定了其他' . $type, 400);
                }

                // 修改member与oauth的绑定关系
                $update_member_oauth_sql = "UPDATE `memberoauth` SET `member_id` = '" . $wait_for_check_member_id . "' WHERE `member_id` = '" . $want_to_bind_member_id . "' AND `oauth_id` = '" . $want_to_bind_oauth_id . "'";

                // 修改第三方登陆的member记录的stats
                $update_member_sql = "UPDATE `member` SET `stats` = '2' WHERE `id` = '" . $want_to_bind_member_id . "'";

                // 修改member_id 以目前的业务需求来说，因为在未绑定手机号码的情况下，禁止购买商品，因此只需要修改favour表中的member_id字段即可
                $update_favour_sql = "UPDATE `alibabafavour` SET `member_id` = '" . $wait_for_check_member_id . "' WHERE `member_id` = '" . $want_to_bind_member_id . "'";
            }
            // 2、没有绑定过任何平台的第三方账号，允许绑定
            else if ($want_to_bind_open_id && $want_to_bind_member_id) {
                // 修改uuid
                $hash_id = new Hashids('ylmall', 16, 'abcdefghijklmnopqrstuvwxyz01234569');
                $uuid = $hash_id->encode($parameters['mobile']);
                $invite_code_list = \models\Business\Member::genInviteCode($uuid);

                // 设置密码
                $password = PwdAss::getInstance(8, true)->HashPassword($parameters['password']);

                // 修改uuid与mobile
                if(count($invite_code_list) > 0){
                    $invite_code = array_shift($invite_code_list);
                    $update_member_sql = "UPDATE `member` SET `uuid` = '" . $invite_code . "', `mobile` = '" . $parameters['mobile'] . "', `password` = '" . $password .
                        "' WHERE `id` = '" . $want_to_bind_member_id . "'";
                }
            }
            // 2、绑定过指定平台的第三方账号，不允许绑定
            else
                throw new OauthException($parameters['mobile'] . '绑定失败', 400);

            if (!empty($update_member_oauth_sql)) {
                $conn->executeQuery($update_member_oauth_sql);
                Registry::get('db_log')->info("修改memberoauth表的sql: " . $update_member_oauth_sql);
                $commit = true;
            }

            if (!empty($update_member_sql)) {
                $conn->executeQuery($update_member_sql);
                Registry::get('db_log')->info("修改member表的sql: " . $update_member_sql);
                $commit = true;
            }

            if (!empty($update_favour_sql)) {
                $conn->executeQuery($update_favour_sql);
                Registry::get('db_log')->info("修改alibabafavour表的sql: " . $update_member_sql);
                $commit = true;
            }

            if ($commit === true)
                $conn->commit();

        } catch (OauthException $e) {
            throw $e;
        } catch (\Exception $e) {
            if ($commit === true)
                $conn->rollback();
            echo $e->getMessage();
            throw new OauthException("手机号码绑定失败", 422);
        }
    }

    /**
     * 第三方账号登陆
     * @param array $parameters
     * @return array|mixed|null
     * @throws MemberException
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws OauthException
     * @throws TransformerException
     * @throws \models\Exception\Service\ServiceException
     */
    static public function login($parameters = []) {
        $oauth = false;
        try {
            // $type = 0, $open_id = "", $union_id = "",
            // $nickname = "", $avatar = "", $sex = 0,
            // $country = "", $province = "", $city = "", $stats = 0
            $oauth = OauthService::getInstance()->create(
                $parameters['type'], $parameters['open_id'],
                isset($parameters['union_id']) ? $parameters['union_id'] : '',
                $parameters['nickname'],
                isset($parameters['avatar']) ? $parameters['avatar'] : '',
                $parameters['sex'],
                isset($parameters['country']) ? $parameters['country'] : '中国',
                isset($parameters['province']) ? $parameters['province'] : '0',
                isset($parameters['city']) ? $parameters['city'] : '0',
                1,
                isset($parameters['access_token']) ? $parameters['access_token'] : '',
                isset($parameters['expired_at']) ? $parameters['expired_at'] : '0000-00-00 00:00:00'
            );
        } catch (\Exception $e) {
            // 非数据库插入失败的情况，抛错
            if ($e->getCode() != ModelDriverException::DRIVER_EXCEPTION_MSG_NO)
                throw $e;

            // 若是淘宝授权登录 则刷access_token

            // oauth信息已存在，查询出来
            $oauth = OauthService::getInstance()->getOne([
                'groupOp' => 'AND', 'rules' => [
                    ['field' => 'type', 'op' => 'eq', 'data' => $parameters['type']],
                    ['field' => 'open_id', 'op' => 'eq', 'data' => $parameters['open_id']]
                ]
            ]);
            if (empty($oauth))
                throw new OauthException(OauthException::OAUTH_IS_NOT_EXISTS, OauthException::OAUTH_IS_NOT_EXISTS_NO);
        }


        // 检测当前oauth是否已经在member表中创建数据
        try {
            // 是否已经在member表、memberoauth表中创建过数据
            $member_oauth = MemberoauthService::getInstance()->getOne(['groupOp' => 'AND', 'rules' => [
                ['field' => 'oauth_id', 'op' => 'eq', 'data' => $oauth['id']]]]);

            // 不存在，创建member数据
            if (empty($member_oauth)) {
                // 将open_id与type合并成字符串再转int后的值来生成uuid
                $hash_id = new Hashids('ylmall', 16, 'abcdefghijklmnopqrstuvwxyz01234569');
                $uuid = $hash_id->encode(self::String2Number($oauth['open_id'] . $oauth['type']));

                /*$member = MemberService::getInstance()->create(
                    $uuid, '0', $oauth['nickname'], '', $oauth['avatar'], 0,
                    '', '', '1', 1, 0.00, 0.00, 0,
                    0, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, date('Y-m-d H:i:s', time()),
                    StringOperation::getIP(), date('Y-m-d H:i:s')
                );*/

                $invite_code_list = Member::genInviteCode($uuid);

                $member = false;
                while($member === false && count($invite_code_list) > 0) {
                    $invite_code = array_shift($invite_code_list);
                    try {
                        $member = MemberService::getInstance()->create(
                            $invite_code, '0', $oauth['nickname'], '', '',
                            isset($oauth['avatar']) && !empty($oauth['avatar']) ? $oauth['avatar'] : '/upload/avatar/default.png',
                            0, '', '1', 1, 1, 0,0.00, 0.00, 0,
                            0, 0, 0, 0, 0, 0,
                            0, 0,0, 1,
                            date('Y-m-d H:i:s', time()), StringOperation::getIP(), date('Y-m-d H:i:s', time())
                        );
                    } catch (Exception $e) {
                        Registry::get('db_log')->err('用户注册失败: ' . $e->getMessage());
                    }
                }

                MemberoauthService::getInstance()->create($member['id'], $oauth['id']);
            } else {
                $member = MemberService::getInstance()->getOne($member_oauth['member_id']);
                if (empty($member['avatar'])) {
                    $member = MemberService::getInstance()->update([
                        'avatar' => isset($oauth['avatar']) && !empty($oauth['avatar']) ? $oauth['avatar'] : '/upload/avatar/default.png'
                    ], $member['id']);
                }
            }

            // 生成jwt
            $member['jwt'] = AccessToken4Api::generateJWT($member['mobile'], $member['id'], $member['parent'], $member['uuid']);

            return $member;
        } catch (\Exception $e) {
            // 唯一字段重复
            if ($e->getCode() == ModelDriverException::DRIVER_EXCEPTION_MSG_NO)
                throw new MemberException(MemberException::MEMBER_CREATE_FAILURE, MemberException::MEMBER_CREATE_FAILURE_NO);

            throw $e;
        }
    }

    /**
     * 手机会员绑定第三方账号
     * @param $parameters
     * @return bool
     * @throws MemberoauthException
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws OauthException
     * @throws TransformerException
     * @throws \models\Exception\Service\ServiceException
     */
    static public function bindOauth($parameters) {
        try {
            // $type = 0, $open_id = "", $union_id = "",
            // $nickname = "", $avatar = "", $sex = 0,
            // $country = "", $province = "", $city = "", $stats = 0
            $oauth = OauthService::getInstance()->create(
                $parameters['type'], $parameters['open_id'],
                isset($parameters['open_id']) ? $parameters['open_id'] : '',
                $parameters['nickname'],
                $parameters['avatar'],
                $parameters['sex'],
                isset($parameters['country']) ? $parameters['country'] : '中国',
                isset($parameters['province']) ? $parameters['province'] : '0',
                isset($parameters['city']) ? $parameters['city'] : '0',
                1,
                isset($parameters['access_token']) ? $parameters['access_token'] : '',
                isset($parameters['expired_at']) ? $parameters['expired_at'] : '0000-00-00 00:00:00'
            );
        } catch (\Exception $e) {
            // 非数据库插入失败的情况，抛错
            if ($e->getCode() != ModelDriverException::DRIVER_EXCEPTION_MSG_NO)
                throw $e;

            // oauth信息已存在，查询出来
            $oauth = OauthService::getInstance()->getOne([
                'groupOp' => 'AND', 'rules' => [
                    ['field' => 'type', 'op' => 'eq', 'data' => $parameters['type']],
                    ['field' => 'open_id', 'op' => 'eq', 'data' => $parameters['open_id']]
                ]
            ]);
            $member_oauth = MemberoauthService::getInstance()->getOne(['groupOp' => 'AND', 'rules' => [
                ['field' => 'oauth_id', 'op' => 'eq', 'data' => $oauth['id']]]]);

            if (!empty($member_oauth)) {
                if ($member_oauth['member_id'] != Registry::get('jwt_info')->id)
                    throw new OauthException(OauthException::OAUTH_HAS_BEEN_BIND, OauthException::OAUTH_HAS_BEEN_BIND_NO);
                if ($member_oauth['member_id'] == Registry::get('jwt_info')->id)
                    throw new OauthException(OauthException::OAUTH_REPEAT_BIND, OauthException::OAUTH_REPEAT_BIND_NO);
            }

        }

        try {
            MemberoauthService::getInstance()->create(Registry::get('jwt_info')->id, $oauth['id']);
            return true;
        } catch (\Exception $e) {
            throw new MemberoauthException(MemberoauthException::MEMBEROAUTH_BIND_FAILURE, MemberoauthException::MEMBEROAUTH_BIND_FAILURE_NO);
        }

    }

    /**
     * 用户所有第三方账号列表
     * @param array $parameters
     * @return array|mixed
     * @throws ModelDriverException
     * @throws ModelException
     * @throws ModelReflectionException
     * @throws ModelSqlException
     * @throws TransformerException
     * @throws \models\Exception\Service\ServiceException
     */
    static public function getOauthList($parameters=[]){
        try {
        $member_list = MemberoauthService::getInstance()->getList(
            isset($parameters['page']) ? $parameters['page'] : 1,
            isset($parameters['page_size']) ? $parameters['page_size'] : 10,
            isset($parameters['sort']) ? $parameters['sort'] : 'desc',
            isset($parameters['order']) ? $parameters['order'] : 'id',
            ['groupOp' => 'AND', 'rules' => [
                ['field' => 'member_id', 'op' => 'eq', 'data' => Registry::get('jwt_info')->id]
               ]
            ]
        );
            $member_list = self::getOauthByModules($member_list);
            return $member_list;
        } catch (ModelDriverException $e) {
            throw $e;
        } catch (ModelException $e) {
            throw $e;
        } catch (ModelReflectionException $e) {
            throw $e;
        } catch (ModelSqlException $e) {
            throw $e;
        } catch (TransformerException $e) {
            throw $e;
        }
    }
    static public function getOauthByModules($modules_list = [])
    {
        $oauth = $oauth_id_info_arr = $oauth_sku_id_arr = [];
        try {
            $oauth_id_arr = array_column($modules_list['data'], 'oauth_id');
            $oauth_id_arr = array_filter(array_unique($oauth_id_arr));

            if (count($oauth_id_arr) == 1)
                $oauth['data'][] = OauthService::getInstance()->getOne(implode('', $oauth_id_arr));
            else if (count($oauth_id_arr) > 1)
                $oauth= OauthService::getInstance()->getList(1, count($oauth_id_arr), 'desc', 'id',
                ['groupOp' => 'AND', 'rules' => [['field' => 'id', 'op' => 'in', 'data' => implode(",", $oauth_id_arr)]]]);

            if (!empty($oauth)) {
//                $oauth_type_id_arr = array_column($oauth['data'], 'type', 'id');
//                $oauth_open_id_arr = array_column($oauth['data'], 'open_id', 'id');
//                $oauth_nickname_id_arr = array_column($oauth['data'], 'nickname', 'id');
//                $oauth_avatar_id_arr = array_column($oauth['data'], 'avatar', 'id');
                $oauth_id_info_arr = array_column($oauth['data'], null, 'id');
            }

        } catch (\Exception $e) {

        } finally {
            foreach ($modules_list['data'] as $k => $v) {
//                $modules_list['data'][$k]['nickname'] = isset($oauth_nickname_id_arr[$v['oauth_id']]) ? $oauth_nickname_id_arr[$v['oauth_id']] : '';
                $modules_list['data'][$k]['oauth_info'] = isset($oauth_id_info_arr[$v['oauth_id']]) ? $oauth_id_info_arr[$v['oauth_id']] : [];
            }
            return $modules_list;
        }
    }

}
